package com.xpec.c4.service.module;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.player.PlayerUserType;
import com.ms.service.db.jedis.JedisManager;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.service.common.ServiceConfig;
import com.xpec.c4.service.module.datacenter.player.db.PlayerMoneyChangeLogModel;
import com.xpec.c4.service.utility.Utility;

/**
 * 服务器信息
 * 
 * @author lipeilin
 * @2011-3-9 下午06:54:34
 * 
 */
public class ServerProperties{
	/**
	 * 
	 */
	private static final Log log = LogFactory.getLog(ServerProperties.class);

	/** account serv api name */
	public static final String ACCOUNT_API_NAME = "account_api.jsp";

	/** account serv api path */
	public static final String ACCOUNT_API_PATH = "/AccountServ/" + ACCOUNT_API_NAME;

	/** provider serv api name */
	public static final String PROVIDER_API_NAME = "provider_api.jsp";

	/** provider serv api path */
	public static final String PROVIDER_API_PATH = "/ProviderServ/" + PROVIDER_API_NAME;

	/**
	 * 記錄所有 module name, 是否存在直接在這裡查詢, 減少一直 loop 造成的效能浪費
	 */
	private static final HashSet<String> moduleNameMap = new HashSet<String>();

	/**
	 * 服务器的模块配置
	 */
	private static final HashMap<String, List<String>> modules = new HashMap<String, List<String>>();

	/**
	 * 用把相同類型的 module name 整理在一起的 map<br>
	 * {@link ModuleName} \ real module name ...
	 */
	private static final HashMap<String, ArrayList<String>> moduleNameGroupMap = new HashMap<String, ArrayList<String>>();

	/**
	 * 属性
	 */
	public static final HashMap<String, String> properties = new HashMap<String, String>();

	/**
	 * 本地服务器名称
	 */
	public static String localServer = "";

	/**
	 * 组通信端口
	 */
	public static int groupTransportPort;

	/**
	 * 组数据共享端口
	 */
	// public static int groupDatalayerPort;

	/**
	 * 组数据共享IP
	 */
	public static String groupIP;
	
	/**
	 * 是否进行消息统计
	 */
	public static boolean msgTypeState;
	
	/**
	 * Server啟動時的時間
	 */
	public static long startTime;
	
	
	public static String BIND_TO_NAME = "not set";
	
	/**
	 * game info 設定檔中指定的所有 module 數量
	 */
	private static int totalModuleCount = 0;
	
	private static String account_allow_login_version = "";
	
	private static String api_allow_IP_list = "";
	
	/**
	 * 允許登入的 client 版本號<br>
	 * {@link PropertyKey#account_allow_login_version}
	 */
	private static final HashSet<String> allowLoginClientVerMap = new HashSet<String>();
	
	/**
	 * 允許call api的ip
	 */
	private static final HashSet<String> APIallowIps = new HashSet<String>();
	
	/**
	 * 機器人用的 tomcat 主機列表
	 */
	private static final ArrayList<String> robotTomcatHostList = new ArrayList<String>();

	/**
	 * 玩家用的 tomcat 主機列表
	 */
	private static final ArrayList<String> playerTomcatHostList = new ArrayList<String>();

	/**
	 * 是否有一般 gateway module 存在
	 */
	private static boolean haveNormalGateway = false;

	/**
	 * 是否有 h5 gateway module 存在
	 */
	private static boolean haveH5Gateway = false;

	/**
	 * 是否儲存機器人的log
	 */
	private static boolean isSaveRobotLog = true;
	
	/**
	 * 不儲存機器人的log時，機器人得分超過指定分數依舊儲存log
	 */
	private static int robotSaveLogScore = 1000000;
	
	/**
	 * 是否儲存機器人的console log
	 */
	private static boolean isSaveRobotConsoleLog = true;
	
	/**
	 * 
	 */
	static {
		loadServerInfo();
		loadGameInfo();
	}

	/**
	 * 
	 */
	private static void loadServerInfo() {
		// 服务器模块配置
		Properties pro = new Properties();
		String filepath = ServiceConfig.getServerInfoFilePath();
		InputStream is = Utility.getFileInputStream(filepath);
			
			// 紀錄Server啟動時的時間
			startTime = System.currentTimeMillis();
			
			try 
			{
				//only support after java 1.6
				//1.5之前就手動進行string轉換吧
				pro.load(new InputStreamReader(is, "UTF-8"));
				is.close();
			}
			catch (IOException e) 
			{
				log.error("failed on Server Properties load:" + e);
				throw new RuntimeException(e);
			}

			Set<Object> keys = pro.keySet();
			for (Object key : keys) {
				String k = ((String) key).trim();
				String value = (pro.getProperty(k)).trim();

				properties.put(k, value);
			}
				

			localServer = pro.getProperty("local_name");
			groupTransportPort = Integer.valueOf(pro.getProperty("group_transport_port"));
			
			//groupDatalayerPort = Integer.valueOf(pro.getProperty("group_datalayer_port"));
			
			groupIP = pro.getProperty("group_ip");
			BIND_TO_NAME = pro.getProperty("bind_to_name");
			
			msgTypeState = Boolean.valueOf(pro.getProperty("MsgStatistic","true"));	

			account_allow_login_version = pro.getProperty(PropertyKey.account_allow_login_version, null);
			if(GameUtility.isHaveString(account_allow_login_version)){
				String[] versions = account_allow_login_version.split(",");
				for (String version : versions) {
					allowLoginClientVerMap.add(version);
				}
			}
			
			api_allow_IP_list = pro.getProperty(PropertyKey.api_allow_IP_list, null);
			if(GameUtility.isHaveString(api_allow_IP_list)){
				String[] ips = api_allow_IP_list.split(",");
				for (String ip : ips) {
					APIallowIps.add(ip);
				}
			}
			
			JedisManager.initialJedisConfiguration(pro);
	
			// 讀取機器人用的 tomcat 主機列表
			for (int idx = 1; idx <= 9999; idx++) {
				String key = PropertyKey.robot_tomcat_host + idx;
				String value = properties.get(key);
	
				if (!GameUtility.isHaveString(value)) {
					break;
				}
	
				robotTomcatHostList.add(value);
			}
	
			// 讀取 player 用的 tomcat 主機列表
			for (int idx = 1; idx <= 9999; idx++) {
				String key = PropertyKey.player_tomcat_host + idx;
				String value = properties.get(key);
	
				if (!GameUtility.isHaveString(value)) {
					break;
				}
	
				playerTomcatHostList.add(value);
			}
			
			// 讀取小樂是否要記錄log, 預設 true
			isSaveRobotLog = getBoolean(PropertyKey.robot_save_log, true);
			
			// 讀取小樂是否要記錄log分數低標, 預設 1000000
			robotSaveLogScore = getInt(PropertyKey.robot_save_log_score, 1000000);
			
			// 讀取小樂是否要記錄console log, 預設 true
			String isSaveRobotConsoleLogStr = pro.getProperty(PropertyKey.robot_save_console_log,"true");
			if(GameUtility.isHaveString(isSaveRobotConsoleLogStr)){
				//只有false完全正確才會關閉
				if(isSaveRobotConsoleLogStr.toLowerCase().equals("false")){
					isSaveRobotConsoleLog = false;
				}
			}
	}
	
	/**
	 * 
	 */
	private static void loadGameInfo() {
		// 服务器模块配置
		Properties pro = new Properties();
		String filepath = ServiceConfig.getGameInfoFilePath();
		InputStream is = Utility.getFileInputStream(filepath);
			
		try {
			pro.load(is);
			is.close();
		}catch (IOException e)	{
				log.error("failed on Server GameProperties load:" + e);
				throw new RuntimeException(e);
		}

		TreeMap<String, String> sortedServerList = new TreeMap<String, String>();
		
		Set<Object> keys = pro.keySet();
		for (Object key : keys) {
			String k = ((String) key).trim();
			String value = (pro.getProperty(k)).trim();
			properties.put(k, value);

			// 如果是服务器信息
			if (k.toLowerCase().startsWith("server")) {
				sortedServerList.put(k, value);
				continue;
			}
		}
			
		HashMap<String, Integer> autoCount = new HashMap<String, Integer>();
		if (GameUtility.getMapSize(sortedServerList) > 0) {
			for (Entry<String, String> servers : sortedServerList.entrySet()) {

				String serverNum = servers.getKey();
				String serverInfo = servers.getValue();

				if (serverInfo != null) {
					String[] ms = serverInfo.split(",");
					ArrayList<String> moduleList = new ArrayList<String>();
					for (String moduleName : ms) {
						if (moduleName.contains("?")) { // ? = >自動生成類型的
							if (!autoCount.containsKey(moduleName)) { // 如果沒有就是第一位客人
								autoCount.put(moduleName, 1);
							} else { // 如果有的話，就把裡面數字加1
								autoCount.put(moduleName, autoCount.get(moduleName) + 1);
							}
							moduleName = moduleName.replace("?", autoCount.get(moduleName).toString());
						}
						
						moduleList.add(moduleName);

						// 整理到相同類型的 map 中
						addToModuleNameGroupMap(moduleName);
					}

					modules.put(serverNum, moduleList);

					//統計有幾個module
					totalModuleCount += ms.length;

					// 將所有的 module name 放入速查 map 中
					for (String moduleName : moduleList) {
						if (moduleNameMap.contains(moduleName)) {
							if (log.isErrorEnabled()) {
								GameUtility.logErrorException(log, "duplicate module name " + moduleName);
							}
							continue;
						}
						moduleNameMap.add(moduleName);
					}
				}
			}
		}

		// 是否有一般 gateway module 存在
		if (getModuleCount(ModuleName.GATEWAY) > 0) {
			haveNormalGateway = true;
		}

		// 是否有 h5 gateway module 存在
		if (getModuleCount(ModuleName.H5GATEWAY) > 0) {
			haveH5Gateway = true;
		}
	}
	
	/**
	 * 加 module name 加到用名稱分類的 map
	 * 
	 * @param moduleName
	 */
	private static void addToModuleNameGroupMap(String moduleName) {
		String[] name = moduleName.split("_");

		if (!GameUtility.isHaveString(name[0])) {
			return;
		}

		String key = name[0];

		// list 有就拿出來，沒有就 new 一個
		ArrayList<String> moduleNameList = null;
		if (!moduleNameGroupMap.containsKey(key)) {
			moduleNameList = new ArrayList<String>();
			moduleNameGroupMap.put(key, moduleNameList);
		} else {
			moduleNameList = moduleNameGroupMap.get(key);
		}

		moduleNameList.add(moduleName);
	}
	
	/**
	 * 用 module name 取得所有同類型的 module name list
	 * 
	 * @param moduleName
	 *            {@link ModuleName}
	 * @return
	 */
	public static ArrayList<String> getModuleNameList(String moduleName) {
		if (!moduleNameGroupMap.containsKey(moduleName)) {
			return null;
		}

		return moduleNameGroupMap.get(moduleName);
	}
	
	/**
	 * 获得指定的数据
	 * 
	 * @param key
	 * @return
	 * 
	 * @author lipeilin
	 * @2011-3-10 下午02:13:53
	 */
	public static String getProperty(String key) {
		String value = properties.get(key);

		writePropertyReadingLog(key, "", value);

		return value;
	}

	/**
	 * 获得指定的数据
	 * 
	 * @param key
	 * @return
	 * 
	 * @author lipeilin
	 * @2011-3-10 下午02:13:53
	 */
	public static int getInt(String key) {

		int value = Integer.valueOf(properties.get(key));

		writePropertyReadingLog(key, "", String.valueOf(value));

		return value;
	}

	/**
	 * 获得BOOLEAN值，如果没有就按默认值
	 * 
	 * @param key
	 * @param defaultValue
	 *            没有时使用的默认值
	 * @return
	 * 
	 * @2011-9-28 下午12:31:52
	 */
	public static boolean getBoolean(String key, boolean defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return Boolean.valueOf(value);
	}

	/**
	 * 获得字符串，如果没有就按默认值
	 * 
	 * @param key
	 * @param defaultValue
	 *            没有时使用的默认值
	 * @return
	 * 
	 * @2011-9-28 下午12:31:52
	 */
	public static String getString(String key, String defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return value;
	}

	/**
	 * 获得Int值，如果没有就按默认值
	 * 
	 * @param key
	 * @param defaultValue
	 *            没有时使用的默认值
	 * @return
	 * 
	 */
	public static int getInt(String key, int defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return Integer.valueOf(value);
	}

	/**
	 * 获得Long值，如果没有就按默认值
	 * 
	 * @param key
	 * @param defaultValue
	 *            没有时使用的默认值
	 * @return
	 * 
	 */
	public static long getLong(String key, long defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return Long.valueOf(value);
	}
	
	public static float getFloat(String key, float defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return Float.valueOf(value);
	}
	
	/**
	 * 获得Long值，如果没有就按默认值
	 * 
	 * @param key
	 * @param defaultValue
	 *            没有时使用的默认值
	 * @return
	 * 
	 */
	public static byte getByte(String key, byte defaultValue) {
		String value = properties.get(key);
		
		writePropertyReadingLog(key, String.valueOf(defaultValue),
				String.valueOf(value));
		
		if (value == null) {
			return defaultValue;
		}
		return Byte.valueOf(value);
	}


	/**
	 * 是否是Account 需使用在指定ServerProperties.localServer之後
	 */
	public static boolean isAccount() {
		for (int i = 1; i <= 100; i++) {
			String serverName = getString(PropertyKey.account_server_name + i, "");
			if (!GameUtility.isHaveString(serverName)) {
				return false;
			}

			if (serverName.equals(ServerProperties.localServer)) {
				return true;
			}
		}

		return false;
	}
	
	/**
	 * 是否是Client需使用在指定ServerProperties.localServer之後 (這應該是機器人在用的?)
	 */
	public static boolean isClient() {
		return "client".equals(
				ServerProperties.localServer);
	}
	

	/**
	 * 
	 * @return
	 */
	public static HashMap<String, List<String>> getModules() {
		return modules;
	}
	
	/**
	 * 取得指定類型的 module 數量
	 * 
	 * @param moduleName
	 *            {@link ModuleName}
	 * @return
	 */
	public static int getModuleCount(String moduleName) {
		ArrayList<String> moduleNameList = getModuleNameList(moduleName);
		if (moduleNameList == null) {
			return 0;
		}

		return moduleNameList.size();
	}

	/**
	 * 確認指定的 module name 是否存在於設定檔中
	 * 
	 * @param moduleName
	 *            {@link ModuleName}
	 * @return
	 */
	public static boolean isModuleNameExist(String moduleName) {
		if (moduleNameMap.contains(moduleName)) {
			return true;
		}

		return false;
	}

	/**
	 * 確認現在是不是所有的 module 都開在同一個 vm 上的模式
	 * 
	 * @return
	 */
	public static boolean isAllModuleStartupInLocal() {
		if (!GameUtility.isHaveString(localServer)) {
			return false;
		}

		if (localServer.equals(ModuleServer.ALL_MODULE_START_UP)) {
			return true;
		}

		return false;
	}

	/**
	 * 顯示讀取 server property 的 log
	 * 
	 * @param propertyKey
	 *            要讀取的 key
	 * @param defaultValue
	 *            預設值，key 取不到值時會用這個傳回
	 * @param returnValue
	 *            用 key 取到的傳回值
	 */
	private static void writePropertyReadingLog(String propertyKey,
			String defaultValue, String returnValue) {
		String tmpString = "read property [" + propertyKey + "] = ";

		if (log.isInfoEnabled()) {
			if (returnValue == null) {
				tmpString += defaultValue;
			} else {
				tmpString += returnValue;
			}

			log.info(tmpString);
		}
	}
	
	/**
	 * 取得server中所指定的module name的名字
	 * 
	 * @param moduleName
	 * 			例如 moduleName = account 會找出所有server中包含account的名字<br>
	 * 			Ex : account_1 account_2
	 * @param isIgnoreCase 是否忽略大小寫搜尋，例如moduleName = ACCount還是會回傳account_1, account_2...
	 */
	public static List<String> getAllModuleNameInServers(String moduleName, boolean isIgnoreCase) {
		List<String> moduleNameList = new ArrayList<String>();
		for (List<String> servers : ServerProperties.getModules().values()) {
			for (String name : servers) {
				if (GameUtility.containString(name, moduleName, isIgnoreCase)) {
					moduleNameList.add(name);
				}
			}
		}
		 return moduleNameList;
	}

	public static int getTotalModuleCount() {
		return totalModuleCount;
	}
	
	/**
	 * 取得指定 module 在 group 裡面的第一個 module name
	 * 
	 * @param moduleName
	 *            {@link ModuleName}, 不帶底線的 module name
	 * @return 有找到, 回傳 module name<br>
	 *         沒找到, 回傳 null
	 */
	public static String getFirstModuleName(String moduleName) {
		if (!GameUtility.isHaveString(moduleName)) {
			return null;
		}

		// 用基本的 module name 去找, 有就一定是第一個 module
		if (moduleNameMap.contains(moduleName)) {
			return moduleName;
		}

		// 再用 xxx_1 下去找
		moduleName += "_1";
		if (moduleNameMap.contains(moduleName)) {
			return moduleName;
		}

		return null;
	}
	
	/**
	 * 是否是目標的module名稱(以忽略大寫小的方式判斷)
	 * @param moduleName
	 * @param targetModuleName @see com.xpec.c4.service.module.ModuleName
	 * @return
	 */
	public static boolean isTargetModule(String moduleName, String targetModuleName){
		if(moduleName == null || targetModuleName == null)
			return false;
		
		String[] checkString = moduleName.split("_");
		//一般module名稱被"_"分割只會有一個或兩個元素
		if(checkString.length == 1){
			if(moduleName.equalsIgnoreCase(targetModuleName)) return true;
		}
		if(checkString.length == 2){
			if(checkString[0].equalsIgnoreCase(targetModuleName)) return true;
		}
		
		//其餘的都視為不是
		return false;
	}
	
	/**
	 * 檢查指定的版本號是否允許登入
	 * 
	 * @param clientVersion
	 * @return
	 */
	public static boolean isAllowLoginClientVersion(String clientVersion) {
		// 沒有設定值, 全都允許登入
		if (allowLoginClientVerMap.size() == 0) {
			return true;
		}

		if (!GameUtility.isHaveString(clientVersion)) {
			return false;
		}

		if (allowLoginClientVerMap.contains(clientVersion)) {
			return true;
		}

		return false;
	}

	/**
	 * 檢查ip是否允許call API
	 * @param ip
	 * @return
	 */
	public static boolean isAPIAllowIp(String ip){
		// 沒有設定值, 全都允許登入
		if (APIallowIps.size() == 0) {
			return true;
		}

		if (!GameUtility.isHaveString(ip)) {
			return false;
		}

		if (APIallowIps.contains(ip)) {
			return true;
		}

		return false;
	}
	
	public static ArrayList<String> getRobottomcathostlist() {
		return robotTomcatHostList;
	}

	public static ArrayList<String> getPlayertomcathostlist() {
		return playerTomcatHostList;
	}

	/**
	 * 判斷是否要儲存 log 的基本檢查
	 * 
	 * @param isRobot
	 * @return
	 */
	private static boolean isNeedSaveLogBasicCheck(boolean isRobot) {
		// 不是機器人一律記錄
		if (!isRobot) {
			return true;
		}
		else{
			return isSaveRobotLog;
		}
	}

	/**
	 * 判斷是否要儲存 log
	 * 
	 * @param logType
	 *            {@link GameLogType}, 如果 logType 是 {@link GameLogType#JACKPOT}
	 *            會忽略參數 isHaveJp, score
	 * @param isRobot
	 *            產生 log 的是否是機器人
	 * @param isHaveJp
	 *            是否有中 JP
	 * @param score
	 *            當局得分
	 * @return
	 */
	public static boolean isNeedSaveLog(GameLogType logType, boolean isRobot, boolean isHaveJp, int score) {
		if (isNeedSaveLogBasicCheck(isRobot)) {
			return true;
		}

		switch (logType) {
		case GAME:
			// 有中 JP 才存
			if (isHaveJp) {
				return true;
			}

			// 得分超過寫 log 的低標要寫 log
			if (score >= robotSaveLogScore) {
				return true;
			}
			break;

		case JACKPOT:
			// 不記 JP log
			break;

		default:
			break;
		}

		return false;
	}

	/**
	 * 是否要記錄 money change log
	 * 
	 * @param moneyChangeLogModel
	 * @return
	 */
	public static boolean isNeedSaveMoneyChangeLog(PlayerMoneyChangeLogModel moneyChangeLogModel) {
		if (moneyChangeLogModel == null) {
			return false;
		}

		// 是不是機器人
		boolean isRobot = (moneyChangeLogModel.getUserType() == PlayerUserType.ROBORT);

		if (isNeedSaveLogBasicCheck(isRobot)) {
			return true;
		}

		// 機器人記錄 log 的分數低標
		int scoreLowLimit = ServerProperties.getInt(PropertyKey.robot_save_log_score, 1000000);

		// 得分超過寫 log 的低標要寫 log
		// 機器人不會用體驗幣, 所以只看 gold
		if (Helper.isGreaterEqual(moneyChangeLogModel.getGoldChangeValue(), new BigDecimal(scoreLowLimit))) {
			return true;
		}

		return false;
	}

	public static HashSet<String> getModulenamemap() {
		return moduleNameMap;
	}

	public static boolean isHaveNormalGateway() {
		return haveNormalGateway;
	}

	public static boolean isHaveH5Gateway() {
		return haveH5Gateway;
	}
	
	/**
	 * 是否要寫入console log
	 * @param userType
	 * @return
	 */
	public static boolean isSaveConsoleLog(int userType){
		if(userType == PlayerUserType.ROBORT){
			return isSaveRobotConsoleLog;
		}
		else{
			return true;
		}
	}
}
